#include "gtest/gtest.h"

#include "recovery/fs/file/File.h"
#include "recovery/fs/file/FileDescriptor.h"
#include "recovery/fs/file/FileAppender.h"
#include "recovery/fs/file/FileRandomReader.h"
#include "recovery/fs/file/FileRandomWriter.h"

using namespace recovery;
using namespace fs;
using namespace file;


class FileTest : public testing::Test {
public:
};

TEST_F(FileTest, testObjectFunctions){
    {
        File f("./abc.exist");
        ASSERT_TRUE(f.exist());
        size_t real = 0;
        ASSERT_TRUE(f.size(real));
        ASSERT_EQ(12ul, real);
    }

    {
        File f("./abc.no-exist");
        ASSERT_FALSE(f.exist());

        ASSERT_TRUE(f.touch());
        ASSERT_TRUE(f.exist());

        const size_t expectSize = 10;
        ASSERT_TRUE(f.truncate(expectSize));
        size_t  size = 0;
        ASSERT_TRUE(f.size(size));
        ASSERT_EQ(expectSize, size);
        
        ASSERT_TRUE(f.remove());
        ASSERT_FALSE(f.exist());
    }
}

TEST_F(FileTest, testAppender){
    std::unique_ptr<FileAppender> tmp;
    ASSERT_TRUE(FileAppender::openExist("./abc.writable", tmp));
    ASSERT_TRUE(tmp);

    std::shared_ptr<FileAppender>  fa(tmp.release());
    size_t size = 0;
    ASSERT_TRUE(fa->file().size(size));
    ASSERT_EQ(0ul, size);

    const std::string msg = "hello world";
    size_t appendedBytes = 0;
    ASSERT_TRUE(fa->append(msg, appendedBytes));
    ASSERT_EQ(msg.size(), appendedBytes);

    ASSERT_TRUE(fa->sync());
    ASSERT_TRUE(fa->file().size(size));
    ASSERT_EQ(msg.size(), size);

    ASSERT_TRUE(fa->append(msg.c_str(), msg.size(), appendedBytes));
    ASSERT_EQ(msg.size(), appendedBytes);

    ASSERT_TRUE(fa->sync());
    ASSERT_TRUE(fa->file().size(size));
    ASSERT_EQ(2*msg.size(), size);

    ASSERT_TRUE(fa->close());
    ASSERT_TRUE(fa->file().truncate());
}
TEST_F(FileTest, testCreateAppender){
    std::unique_ptr<FileAppender> tmp;
    ASSERT_TRUE(FileAppender::create("./abc.appender.create", tmp));
    ASSERT_TRUE(tmp);

    std::shared_ptr<FileAppender>  fa(tmp.release());
    size_t size = 0;
    ASSERT_TRUE(fa->file().size(size));
    ASSERT_EQ(0ul, size);

    const std::string msg = "hello world";
    size_t appendedBytes = 0;
    ASSERT_TRUE(fa->append(msg, appendedBytes));
    ASSERT_EQ(msg.size(), appendedBytes);

    ASSERT_TRUE(fa->sync());
    ASSERT_TRUE(fa->file().size(size));
    ASSERT_EQ(msg.size(), size);

    ASSERT_TRUE(fa->append(msg.c_str(), msg.size(), appendedBytes));
    ASSERT_EQ(msg.size(), appendedBytes);

    ASSERT_TRUE(fa->sync());
    ASSERT_TRUE(fa->file().size(size));
    ASSERT_EQ(2*msg.size(), size);

    ASSERT_TRUE(fa->close());
    ASSERT_TRUE(fa->file().remove());
}

TEST_F(FileTest, testRandomRead){
    std::unique_ptr<FileRandomReader> reader;
    ASSERT_TRUE(FileRandomReader::openExist("./abc.exist", reader));
    
    size_t readBytes = 0;
    char buf[5];
    ASSERT_TRUE(reader->read(buf, sizeof(buf), readBytes));
    ASSERT_EQ(sizeof(buf), readBytes);

    reader->close();
}

TEST_F(FileTest, testRandomWrite){
    std::unique_ptr<FileRandomWriter> writer;
    ASSERT_TRUE(FileRandomWriter::openExist("./abc.writable", writer));

    const std::string msg("hello world");
    size_t writedBytes = 0;
    ASSERT_TRUE(writer->write(msg, writedBytes));
    ASSERT_EQ(msg.size(), writedBytes);

    ASSERT_TRUE(writer->sync());
    size_t size = 0;
    ASSERT_TRUE(writer->file().size(size));
    ASSERT_EQ(msg.size(), size);

    ASSERT_TRUE(writer->close());
    
    ASSERT_TRUE(writer->file().truncate());
    ASSERT_TRUE(writer->file().exist());
    ASSERT_TRUE(writer->file().size(size));
    ASSERT_EQ(0ul, size);
}

TEST_F(FileTest, testCreateRandomWrite){
    std::unique_ptr<FileRandomWriter> writer;
    ASSERT_TRUE(FileRandomWriter::create("./abc.create.writable", writer));

    const std::string msg("hello world");
    size_t writedBytes = 0;
    ASSERT_TRUE(writer->write(msg, writedBytes));
    ASSERT_EQ(msg.size(), writedBytes);

    ASSERT_TRUE(writer->sync());
    size_t size = 0;
    ASSERT_TRUE(writer->file().size(size));
    ASSERT_EQ(msg.size(), size);

    ASSERT_TRUE(writer->close());
    
    writer->file().remove();
    ASSERT_FALSE(writer->file().exist());
}
